package com.denlaku.longan.aspect;

import com.denlaku.longan.core.Current;
import com.denlaku.longan.http.Return;
import com.denlaku.longan.service.UserService;
import com.denlaku.longan.util.Sets;
import com.denlaku.longan.vo.Base;
import com.denlaku.longan.vo.TenantBase;
import com.denlaku.longan.vo.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Controller切面
 *
 * @author tianx
 */
@Component
@Aspect
@Slf4j
public class ControllerAspect {
	@Autowired
	private UserService userService;

	/**
	 * 对@VarBy注解的方法返回值进行处理，收集userId并返回用户基础信息
	 *
	 * @param result 方法返回结果
	 */
	@AfterReturning(value = "@annotation(com.denlaku.longan.anno.VarBy)", returning = "result")
	public void afterReturning(Object result) {
		if (!(result instanceof Return<?> rtn)) {
			return;
		}
        Object data = rtn.getData();
		if (data == null) {
			return;
		}
		Set<Long> userIds = Sets.ofSize(16);
		if (data instanceof Collection<?>) {
			((Collection<?>) data).forEach(obj -> collectUserIds(userIds, obj));
		} else {
			collectUserIds(userIds, data);
		}
		Map<Long, User> umap = rtn.getUmap();
		final Map<Long, User> userMap = new HashMap<>(MapUtils.emptyIfNull(umap));
		Set<Long> idSet = userIds.stream().filter(userId -> !userMap.containsKey(userId)).collect(Collectors.toSet());
		userMap.putAll(userService.listBaseInfoMap(idSet));
		rtn.setUmap(userMap);
	}

	@Before(value = "@within(org.springframework.web.bind.annotation.RestController)")
	public void before(JoinPoint jp) {
		Object[] args = jp.getArgs();
		if (args == null || args.length == 0) {
			return;
		}
		Long tenantId = Current.getTenantId();
		for (Object arg : args) {
			if (arg instanceof Collection<?>) {
				((Collection<?>) arg).forEach(item -> setTenantId(item, tenantId));
			} else {
				setTenantId(arg, tenantId);
			}
		}
	}

	private void setTenantId(Object arg, Long tenantId) {
		if (arg instanceof TenantBase) {
			((TenantBase) arg).setTenantId(tenantId);
		}
	}

	private void collectUserIds(Set<Long> userIds, Object obj) {
		if (!(obj instanceof Base base)) {
			return;
		}
        Long createdBy = base.getCreatedBy();
		if (createdBy != null) {
			userIds.add(createdBy);
		}
        Long updatedBy = base.getUpdatedBy();
		if (updatedBy != null) {
			userIds.add(updatedBy);
		}
	}

}
